﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using GesturePlayer.SongLibrary;
using System.Windows.Threading;
using GesturePlayer.Utitlities;
using WiimoteLib;
using GestureLib;

namespace GesturePlayer
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private SongLibraryFetcher _fetcher = new SongLibraryFetcher();
        private int _artistIndex, _albumIndex, _titleIndex;

        private DispatcherTimer _timer;

        private GestureLib.GestureLib _gl;

        private double? _savedVolume;
        private bool _shuffle, _repeat;

        private enum Direction
        {
            None,
            Next,
            Previous,
        }


        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            _fetcher.Folder = @"F:\TestMusic";
            _fetcher.FetchAllSongs();

            _timer = new DispatcherTimer();
            _timer.Interval = new TimeSpan(0, 0, 0, 0, 100);
            _timer.Tick += new EventHandler(timer_Tick);

            SwitchAlbum(Direction.None);

            /***************************************************/

            #region General Settings
            _gl = new GestureLib.GestureLib();

            _gl.GestureDevice = CreateMouseDevice();
            //_gl.GestureDevice = CreateWiiDevice();
            _gl.ImpersonatingMouse = true;
            _gl.ScreenBounds = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
            _gl.Recording.EventFilterNumber = 1;
            //_gl.Recording.ShowOverlayWhenBeginRecording = true;
            #endregion

            #region Algorithms
            IGestureAlgorithm shakingTopBottom = new ShakingTopBottomGestureAlgorithm();
            IGestureAlgorithm shakingLeftRight = new ShakingLeftRightGestureAlgorithm();
            IGestureAlgorithm leftRight = new LeftRightAccelerationGestureAlgorithm();
            IGestureAlgorithm rightLeft = new RightLeftAccelerationGestureAlgorithm();
            IGestureAlgorithm roll = new RollGestureAlgorithm();
            IGestureAlgorithm jab = new JabGestureAlgorithm();
            IGestureAlgorithm pitch = new PitchGestureAlgorithm();

            IGestureAlgorithm pointerLeftRight = new LeftRightGestureAlgorithm();
            IGestureAlgorithm pointerTopBottom = new TopBottomGestureAlgorithm();
            IGestureAlgorithm pointerBottomTop = new BottomTopGestureAlgorithm();
            IGestureAlgorithm pointerRightLeft = new RightLeftGestureAlgorithm();
            IGestureAlgorithm pointerDiagonalBottomLeftTopRight = new DiagonalBottomLeftTopRightGestureAlgorithm();
            IGestureAlgorithm pointerDiagonalTopLeftBottomRight = new DiagonalTopLeftBottomRightGestureAlgorithm();
            IGestureAlgorithm pointerDiagonalTopRightBottomLeft = new DiagonalTopRightBottomLeftGestureAlgorithm();
            IGestureAlgorithm pointerDiagonalBottomRightTopLeft = new DiagonalBottomRightTopLeftGestureAlgorithm();
            #endregion

            #region Actions
            MethodInvokerGestureAction switchVolumeStateAction = 
                new MethodInvokerGestureAction() { MethodDelegate = new MethodInvokerGestureAction.NoArgumentsMethodInvokerDelegate(SwitchVolume), Name = "SwitchVolume" };

            MethodInvokerGestureAction switchRepeatStateAction =
                            new MethodInvokerGestureAction() { MethodDelegate = new MethodInvokerGestureAction.NoArgumentsMethodInvokerDelegate(SwitchRepeat), Name = "SwitchVolume" };
            
            MethodInvokerGestureAction switchShuffleStateAction =
                new MethodInvokerGestureAction() { MethodDelegate = new MethodInvokerGestureAction.NoArgumentsMethodInvokerDelegate(SwitchShuffle), Name = "SwitchVolume" };

            MethodInvokerGestureAction switchNextTitleStateAction =
                new MethodInvokerGestureAction() { MethodDelegate = new MethodInvokerGestureAction.NoArgumentsMethodInvokerDelegate(SwitchNextTitle), Name = "NextTitle" };
            
            MethodInvokerGestureAction switchPreviousTitleStateAction =
                new MethodInvokerGestureAction() { MethodDelegate = new MethodInvokerGestureAction.NoArgumentsMethodInvokerDelegate(SwitchPreviousTitle), Name = "PreviousTitle" };
            #endregion

            #region Add Algorithms and Actions
            _gl.AvailableGestureAlgorithms.Add(shakingTopBottom);
            _gl.AvailableGestureAlgorithms.Add(shakingLeftRight);
            _gl.AvailableGestureAlgorithms.Add(leftRight);
            _gl.AvailableGestureAlgorithms.Add(rightLeft);
            _gl.AvailableGestureAlgorithms.Add(roll);
            _gl.AvailableGestureAlgorithms.Add(jab);
            _gl.AvailableGestureAlgorithms.Add(pitch);

            _gl.AvailableGestureAlgorithms.Add(pointerLeftRight);
            _gl.AvailableGestureAlgorithms.Add(pointerTopBottom);
            _gl.AvailableGestureAlgorithms.Add(pointerBottomTop);
            _gl.AvailableGestureAlgorithms.Add(pointerRightLeft);
            _gl.AvailableGestureAlgorithms.Add(pointerDiagonalBottomLeftTopRight);
            _gl.AvailableGestureAlgorithms.Add(pointerDiagonalTopLeftBottomRight);
            _gl.AvailableGestureAlgorithms.Add(pointerDiagonalBottomRightTopLeft);
            _gl.AvailableGestureAlgorithms.Add(pointerDiagonalTopRightBottomLeft);
            
            _gl.AvailableGestureActions.Add(switchVolumeStateAction);
            _gl.AvailableGestureActions.Add(switchRepeatStateAction);
            _gl.AvailableGestureActions.Add(switchShuffleStateAction);
            #endregion

            #region Add trained Gestures
            TrainedGesture switchVolumeStateGesture = new TrainedGesture();
            switchVolumeStateGesture.GestureAlgorithms.Add(shakingTopBottom);
            switchVolumeStateGesture.GestureActions.Add(switchVolumeStateAction);
            switchVolumeStateGesture.Name = "SwitchVolumeStateGesture";
            _gl.TrainedGestures.Add(switchVolumeStateGesture);

            TrainedGesture switchRepeatStateGesture = new TrainedGesture();
            switchRepeatStateGesture.GestureAlgorithms.Add(roll);
            switchRepeatStateGesture.GestureActions.Add(switchRepeatStateAction);
            switchRepeatStateGesture.Name = "SwitchRepeatStateGesture";
            _gl.TrainedGestures.Add(switchRepeatStateGesture);

            TrainedGesture switchShuffleStateGesture = new TrainedGesture();
            switchShuffleStateGesture.GestureAlgorithms.Add(shakingLeftRight);
            switchShuffleStateGesture.GestureActions.Add(switchShuffleStateAction);
            switchShuffleStateGesture.Name = "SwitchShuffleStateGesture";
            _gl.TrainedGestures.Add(switchShuffleStateGesture);

            TrainedGesture switchNextTitleGesture = new TrainedGesture();
            switchNextTitleGesture.GestureAlgorithms.Add(leftRight);
            switchNextTitleGesture.GestureActions.Add(switchNextTitleStateAction);
            switchNextTitleGesture.Name = "SwitchNextTitleStateGesture";
            _gl.TrainedGestures.Add(switchNextTitleGesture);

            TrainedGesture switchPreviousTitleGesture = new TrainedGesture();
            switchPreviousTitleGesture.GestureAlgorithms.Add(rightLeft);
            switchPreviousTitleGesture.GestureActions.Add(switchPreviousTitleStateAction);
            switchPreviousTitleGesture.Name = "SwitchPreviousTitleStateGesture";
            _gl.TrainedGestures.Add(switchPreviousTitleGesture);
            #endregion

            _gl.Recording.RecordingFinished += new EventHandler(Recording_RecordingFinished);
        }

        private void Recording_RecordingFinished(object sender, EventArgs e)
        {
            PointTendenceAnalyzer pointTendenceAnalyzer = _gl.Recording.RecognizeRecording(_gl.Recording.RecordedPointerGestureStates);
            GestureAlgorithmCollection matchedPointerAlgorithmes = pointTendenceAnalyzer.MatchedGestureAlgorithms;
            TrainedGesture trainedGesture = _gl.TrainedGestures.GetTrainedGestureByMatchedAlgorithms(matchedPointerAlgorithmes);
            if (trainedGesture != null)
            {
                trainedGesture.GestureActions.ForEach(ga => ga.Execute());
            }

            ////////////////////////////////////////////////////////////////////////

            GestureAlgorithmCollection matchedAccelerationAlgorithmes = _gl.Recording.RecognizeRecording(_gl.Recording.RecordedAccelerationGestureStates);
            
            trainedGesture = _gl.TrainedGestures.GetTrainedGestureByMatchedAlgorithms(matchedAccelerationAlgorithmes);
            if (trainedGesture != null)
            {
                trainedGesture.GestureActions.ForEach(ga => ga.Execute());
            };
        }

        private void SwitchAlbum(Direction direction)
        {
            int delta = 0;

            if (direction == Direction.Next)
            {
                delta = 1;
            }
            else if (direction == Direction.Previous)
            {
                delta = -1;
            }

            _albumIndex += delta;

            if (_albumIndex >= _fetcher.Artists[_artistIndex].Albums.Count)
            {
                _artistIndex = CalculateNewIndex(_artistIndex, 1, _fetcher.Artists.Count);
                _albumIndex = 0;
                _titleIndex = 0;
            }
            else if (_albumIndex < 0)
            {
                _artistIndex = CalculateNewIndex(_artistIndex, -1, _fetcher.Artists.Count);
                _albumIndex = _fetcher.Artists[_artistIndex].Albums.Count - 1;
                _titleIndex = 0;
            }

            _titleIndex = 0;

            Album album = _fetcher.Artists[_artistIndex].Albums[_albumIndex];

            BitmapImage image;
            if (album.CoverPath == null)
                image = new BitmapImage(new Uri("pack://application:,,,/ImageResources/QuestionMark.png"));
            else
                image = new BitmapImage(new Uri(album.CoverPath));
            imgCover.Source = image;

            lblAlbum.Content = album.Name + " (" + album.Titles.Count + " Titel)";
            lblArtist.Content = album.Artist.Name + " (" + album.Artist.Albums.Count + (album.Artist.Albums.Count == 1 ? " Album)" : " Alben)");

            SwitchTitle(Direction.None);
        }

        private void SwitchTitle(Direction direction)
        {
            int delta = 0;

            if (direction == Direction.Next)
            {
                delta = 1;
            }
            else if (direction == Direction.Previous)
            {
                delta = -1;
            }

            _titleIndex += delta;

            if (_titleIndex >= _fetcher.Artists[_artistIndex].Albums[_albumIndex].Titles.Count)
            {
                SwitchAlbum(Direction.Next);
                _titleIndex = 0;
            }
            else if (_titleIndex < 0)
            {
                SwitchAlbum(Direction.Previous);
                _titleIndex = _fetcher.Artists[_artistIndex].Albums[_albumIndex].Titles.Count - 1;
            }

            Title title = _fetcher.Artists[_artistIndex].Albums[_albumIndex].Titles[_titleIndex];
            lblTrackTitle.Content = title.Position + ". " + title.Name;

            meAudioFile.Source = new Uri(title.FileName);
        }

        private int CalculateNewIndex(int currentIndex, int delta, int maximum)
        {
            int newIndex = currentIndex + delta;

            if (newIndex >= maximum)
            {
                newIndex -= maximum;
            }
            else if (newIndex < 0)
            {
                newIndex += maximum;
            }
            
            return newIndex;
        }

        private void btnNextAlbum_Click(object sender, RoutedEventArgs e)
        {
            SwitchAlbum(Direction.Next);
        }

        private void btnPreviousAlbum_Click(object sender, RoutedEventArgs e)
        {
            SwitchAlbum(Direction.Previous);
        }

        private void btnPreviousTitle_Click(object sender, RoutedEventArgs e)
        {
            SwitchTitle(Direction.Previous);
        }

        private void btnNextTitle_Click(object sender, RoutedEventArgs e)
        {
            SwitchTitle(Direction.Next);
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            sldTrackPosition.Value = meAudioFile.Position.TotalMilliseconds;

            if (meAudioFile.NaturalDuration.HasTimeSpan)
            {
                lblTime.Content = string.Format("{0}/{1}", meAudioFile.Position.ToReadableTime(), meAudioFile.NaturalDuration.TimeSpan.ToReadableTime());
            }
        }

        private void meAudioFile_MediaOpened(object sender, RoutedEventArgs e)
        {
            sldTrackPosition.Minimum = 0.0;
            sldTrackPosition.Value = 0.0;

            if (meAudioFile.NaturalDuration.HasTimeSpan)
            {
                sldTrackPosition.Maximum = meAudioFile.NaturalDuration.TimeSpan.TotalMilliseconds;
                lblTime.Content = string.Format("{0}/{1}", meAudioFile.Position.ToReadableTime(), meAudioFile.NaturalDuration.TimeSpan.ToReadableTime());
            }
            _timer.Start();
        }

        

        #region GestureLib stuff

        private WiiGestureDevice CreateWiiDevice()
        {
            Wiimote wiimote = new Wiimote();
            wiimote.Connect();
            wiimote.SetReportType(Wiimote.InputReport.IRAccel, true);
            wiimote.SetLEDs(false, true, true, false);

            //attach create WiiGestureDevice to GestureLib
            WiiGestureDevice wiiDevice = new WiiGestureDevice();
            wiiDevice.Dispatcher = Application.Current.Dispatcher;
            wiiDevice.Wiimote = wiimote;

            return wiiDevice;
        }

        private MouseGestureDevice CreateMouseDevice()
        {
            MouseGestureDevice mouseDevice = new MouseGestureDevice(
                new System.Drawing.Size(
                    (int)System.Windows.SystemParameters.PrimaryScreenWidth,
                    (int)System.Windows.SystemParameters.PrimaryScreenHeight),
                true);

            return mouseDevice;
        }
        
        private void SwitchVolume()
        {
            if (!_savedVolume.HasValue)
            {
                _savedVolume = meAudioFile.Volume;
                meAudioFile.Volume = 0.0;
            }
            else
            {
                meAudioFile.Volume = _savedVolume.Value;
                _savedVolume = null;
            }     

            System.Diagnostics.Debug.WriteLine("Volume");
        }

        private void SwitchRepeat()
        {
            string imageFileName = string.Empty;

            if (_shuffle)
            {
                imageFileName = "repeat_off.png";
            }
            else
            {
                imageFileName = "repeat_on.png";
            }

            BitmapImage image = new BitmapImage(new Uri("pack://application:,,,/ImageResources/" + imageFileName));
            imgRepeat.Source = image;
            _shuffle = !_shuffle;
        }

        private void SwitchShuffle()
        {
            string imageFileName = string.Empty;

            if (_shuffle)
            {
                imageFileName = "shuffle_off.png";
            }
            else
            {
                imageFileName = "shuffle_on.png";
            }

            BitmapImage image = new BitmapImage(new Uri("pack://application:,,,/ImageResources/" + imageFileName));
            imgShuffle.Source = image;
            _shuffle = !_shuffle;
        }

        private void SwitchNextTitle()
        {
            SwitchTitle(Direction.Next);
        }

        private void SwitchPreviousTitle()
        {
            SwitchTitle(Direction.Previous);
        }

        #endregion
    }
}
